//
// Created by Zhangwj on 2023/9/18.
//

#include "Lexer.h"
#include "../Utils/charHelper.h"


#include <utility>
#include <iostream>


std::map<std::string, LexType> reserveWordsMap = {
        {"main",     LexType::MAINTK},
        {"const",    LexType::CONSTTK},
        {"int",      LexType::INTTK},
        {"break",    LexType::BREAKTK},
        {"continue", LexType::CONTINUETK},
        {"if",       LexType::IFTK},
        {"else",     LexType::ELSETK},
        {"!",        LexType::NOT},
        {"&&",       LexType::AND},
        {"||",       LexType::OR},
        {"for",      LexType::FORTK},
        {"getint",   LexType::GETINTTK},
        {"printf",   LexType::PRINTFTK},
        {"return",   LexType::RETURNTK},
        {"+",        LexType::PLUS},
        {"-",        LexType::MINU},
        {"void",     LexType::VOIDTK},
        {"*",        LexType::MULT},
        {"/",        LexType::DIV},
        {"%",        LexType::MOD},
        {"<",        LexType::LSS},
        {"<=",       LexType::LEQ},
        {">",        LexType::GRE},
        {">=",       LexType::GEQ},
        {"==",       LexType::EQL},
        {"!=",       LexType::NEQ},
        {"=",        LexType::ASSIGN},
        {";",        LexType::SEMICN},
        {",",        LexType::COMMA},
        {"(",        LexType::LPARENT},
        {")",        LexType::RPARENT},
        {"[",        LexType::LBRACK},
        {"]",        LexType::RBRACK},
        {"{",        LexType::LBRACE},
        {"}",        LexType::RBRACE},
};

bool isReserveWord(const std::string &token) {
    auto item = reserveWordsMap.find(token);
    return item != reserveWordsMap.end();
}

Lexer::Lexer(std::string source) : source(std::move(source)) {
    curPos = 0;
    token = "";
    lineNum = 1;
}

std::tuple<LexType, std::string> Lexer::next() {
    token = "";
    moveToNBC();
    if (curPos >= source.size()) {
        return {LexType::LEXER_END, token};
    }
    char c = getNextChar();
    if (isAlphaOrUnderscore(c)) {
        c = tokenAppendAndGetNext(c);
        while (isAlnumOrUnderscore(c)) {
            c = tokenAppendAndGetNext(c);
        }
        backwardCurPos();
        if (isReserveWord(token)) {
            return {reserveWordsMap.find(token)->second, token};
        }
        return {LexType::IDENFR, token};
    } else if (c == '0') {
        tokenAppend(c);
        return {LexType::INTCON, token}; // 如果有前导0，直接算作单独的0并返回
    } else if (isNonZeroDigit(c)) {
        c = tokenAppendAndGetNext(c);
        while (std::isdigit(c)) {
            c = tokenAppendAndGetNext(c);
        }
        backwardCurPos();
        return {LexType::INTCON, token};
    } else if (c == '"') {
        while (true) {
            c = tokenAppendAndGetNext(c);
            if (isNormalChar(c)) {
                continue;
            } else if (c == '%') {
                c = tokenAppendAndGetNext(c);
                if (c == 'd') {
                    continue;
                } else {
                    backwardCurPos();
                    return {LexType::LEXER_ERROR, token};
                }
            } else if (c == '\\') {
                c = tokenAppendAndGetNext(c);
                if (c == 'n') {
                    continue;
                } else {
                    backwardCurPos();
                    return {LexType::LEXER_ERROR, token};
                }
            } else if (c == '"') {
                tokenAppend(c);
                return {LexType::STRCON, token};
            } else {
                backwardCurPos();
                return {LexType::LEXER_ERROR, token};
            }
        }
    } else if (c == '&') {
        c = tokenAppendAndGetNext(c);
        if (c == '&') {
            tokenAppend(c);
            return {reserveWordsMap.find(token)->second, token};
        } else {
            backwardCurPos();
            return {LexType::LEXER_ERROR, token};
        }
    } else if (c == '|') {
        c = tokenAppendAndGetNext(c);
        if (c == '|') {
            tokenAppend(c);
            return {reserveWordsMap.find(token)->second, token};
        } else {
            backwardCurPos();
            return {LexType::LEXER_ERROR, token};
        }
    } else if (c == '+' || c == '-' || c == '*' || c == '%' || c == ';' || c == ',' || c == '(' ||
               c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
        tokenAppend(c);
        return {reserveWordsMap.find(token)->second, token};
    } else if (c == '<' || c == '>' || c == '=' || c == '!') {
        c = tokenAppendAndGetNext(c);
        if (c == '=') {
            tokenAppend(c);
        } else {
            backwardCurPos();
        }
        return {reserveWordsMap.find(token)->second, token};
    } else if (c == '/') {
        c = tokenAppendAndGetNext(c);
        if (c == '/') {
            c = tokenAppendAndGetNext(c);
            while (c != '\n' && c != 0) {
                c = tokenAppendAndGetNext(c);
            }
            lineNum++;
            return next();
        } else if (c == '*') {
            c = tokenAppendAndGetNext(c);
            while (c != 0) {
                while (c != '*' && c != 0) {
                    if (c == '\n') {
                        lineNum++;
                    }
                    c = tokenAppendAndGetNext(c);
                }
                while (c == '*') {
                    c = tokenAppendAndGetNext(c);
                }
                if (c == '/') {
                    return next();
                }
            }
        } else {
            backwardCurPos();
            return {reserveWordsMap.find(token)->second, token};
        }
    }
    tokenAppend(c);
    return {LexType::LEXER_ERROR, token};
}

char Lexer::getNextChar() {
    if (curPos < source.size()) {
        return source[curPos++];
    }
    return 0;
}

void Lexer::tokenAppend(char c) {
    token.append(1, c);
}

void Lexer::backwardCurPos() {
    if (curPos > 0) {
        curPos--;
    }
}

void Lexer::moveToNBC() {
    while (curPos < source.size() && std::isspace(source[curPos])) {
        if (source[curPos] == '\n') {
            lineNum++;
        }
        curPos++;
    }
}

char Lexer::tokenAppendAndGetNext(char c) {
    token.append(1, c);
    return getNextChar();
}


